from flask import Flask, request, session, redirect, url_for, render_template
import re
from hashlib import md5
from time import time
from bisect import bisect
import pickle
import simplecrypt
import json
from send_mail import send_mail


class User:
    def __init__(
        self,
        username='',
        passhash='',
        email='',
        college='',
        score=0,
        Qno=0,
        token='',
        lasttime=0
    ):
        self.username = username  # 0
        self.passhash = passhash  # 1
        self.email = email        # 2
        self.college = college    # 3
        self.score = score        # 4
        self.Qno = Qno            # 5
        self.token = token        # 6
        self.lasttime = lasttime  # 7

    def __repr__(self):
        return (
            "<User("
            "username='%s', passhash='%s',"
            " email='%s', college='%s', score='%s',"
            " Qno='%s', token='%s', lasttime='%s'"
            ")>") % (
            self.username,
            self.passhash,
            self.email,
            self.college,
            self.score,
            self.Qno,
            self.token,
            self.lasttime
        )

    def run_checks(self):
        if all([
            self._username_check(),
            self._email_check(),
        ]):
            # ------------------------------------------------
            # FIXME
            # second layer of checks, scan through the strings
            # for SQL commands. but for now
            return True
        else:
            return True  # temporary

    def _username_check(self):
        if re.compile('[^_\-\.0-9A-Za-z]').findall(self.username):
            return False
        else:
            return True

    def _email_check(self):
        email = str(self.email)
        # -----------------------------------------------------------------
        # FIXME replace this simple email mathcher with a more reliable one
        matches = re.compile(
            '^[0-9A-Za-z\._\-]+@[0-9A-Za-z]+.[A-Za-z]+$').findall(email)
        if matches and matches == [email]:
            return True
        else:
            return False


class dbhandler:
    def __init__(self):
        self.engine = pickle.load(
            open('/home/eightnoteight/concetto-2k14-nonsenso/nonsenso.pickle', 'r'))
        self.Qdiff = [1, 3, 4, 3, 2, 4, 4, 4, 5, 4, 5, 4, 5, 4, 3, 1, 3, 4]
        self.Qscore = [15, 60, 180, 900, 3600, 10800, 86400, 259200, 1296000]

    def getuser(self, username='eightnoteight'):
        for x in xrange(len(self.engine)):
            if self.engine[x][0] == username:
                return self.engine[x]
        return None

    def editscore(self, username='eightnoteight'):
        results = self.getuser(username)
        if results:
            u = results
            u[4] += self.Qdiff[u[5]] * (10-bisect(self.Qscore, time()-u[7]))
            u[7] = time()
            u[5] += 1
            pickle.dump(
                self.engine,
                open('/home/eightnoteight/concetto-2k14-nonsenso/nonsenso.pickle', 'w'))
        else:
            raise ValueError

    def setstarttime(self, username):
        results = self.getuser(username)
        if results:
            results[7] = int(time())
            pickle.dump(
                self.engine,
                open('/home/eightnoteight/concetto-2k14-nonsenso/nonsenso.pickle', 'w'))
        else:
            raise ValueError

    def getranklist(self):
        self.engine.sort(key=lambda x: x[4], reverse=True)
        return self.engine

    def createuser(
        self,
        username='eightnoteight',
        passhash='7f17f3786af548af4e2abff1f19849a3',
        email='mr.eightnoteight@gmail.com',
        college='Indian School of Mines',
        token=None,
        score=0,
        Qno=0,
        lasttime=0
    ):
        if self.getuser(username=username):
            raise ValueError
        print 'passed 120'
        token = token or md5(username+passhash).hexdigest()
        t = User(
            username=username,
            passhash=passhash,
            email=email,
            college=college,
            score=0,
            token=token,
            lasttime=0,
            Qno=0
        )
        if not t.run_checks():
            raise ValueError
        try:
            cipher = simplecrypt.encrypt(app.secret_key, json.dumps([
                username,
                passhash,
                email,
                college,
                token
            ]))
        except:
            raise ValueError
        # TODO: don't forget to change this to original domain and original email
        if not send_mail(
            'http://eightnoteight.pythonanywhere.com/confirm?token='+cipher,
            'mr.eightnoteight@gmail.com',
            email
        ):
            raise ValueError

    def confirm_createuser(self, t):
        self.engine.append([
            t[0],
            t[1],
            t[2],
            t[3],
            0,
            0,
            t[4],
            0
        ])
        pickle.dump(
            self.engine,
            open('/home/eightnoteight/concetto-2k14-nonsenso/nonsenso.pickle', 'w'))


# nonsenso s1ite
app = Flask(__name__)
app.config.from_object(__name__)
app.debug = True
app.secret_key = '8621bfe41afb0e4cf0496ce0e8286571'
db = dbhandler()
questionanswers = ['2cbdaf9ce47a2c4adbd1fd578a192c58',
                   'df119d4f6efe038ae92539fab8521779',
                   'ed03bb54ddfaf5a6a45c810a4b1c80c2',
                   '35d6d33467aae9a2e3dccb4b6b027878',
                   '37d312c7ac7f52710571d1352aa0bab0',
                   '9022a153e6190f10d9b57aa4232b8aea',
                   'ed76db9ef9c5d63d0661ed3a620adc32',
                   '75b28842126ba3f7539a1ed8177def3f',
                   '7d596e63a5fb123d605bc09dc7f96fa5',
                   'f08109c7959e4951d81c09b37e829805',
                   '50ca90bc2cd39a03a2eb0a2f7a1761aa',
                   '7854295d64a34247790d92f45ceb49ba',
                   'dcb7eecaffba77d9f7f556c34146d30b',
                   '17639102ec928ab93cd0b5e3198f48b7',
                   'd5d52bc0ae7fc5ae04627f5d5ec81f0a',
                   '8c6f13bb3630ebc5f39e5961ec305aec',
                   '08238953fe0f6dd13f10714ebfbcb9f9',
                   '23678db5efde9ab46bce8c23a6d91b50']


@app.route('/')
def index():
    if 'username' in session:
        return app.send_static_file('index.html')
    return app.send_static_file('home.html')


@app.route('/rankings.html')
@app.route('/ranklist.html')
@app.route('/ranklist')
def ranks():
    users = db.getranklist()
    return render_template('ranklist.html', users=users)


@app.route('/quiz', methods=['GET', 'POST'])
def quiz():
    if request.method == 'POST':
        print str(int(db.getuser(session['username'])[5])+1)
        return redirect(
            url_for('quiz',
                    question=str(int(db.getuser(session['username'])[5])+1),
                    answer=request.form['answer']))
    if 'username' not in session:
        return redirect(
            url_for('login'))
    if int(request.args['question']) != db.getuser(session['username'])[5]+1:
        return redirect(
            url_for('quiz', question=str(db.getuser(session['username'])[5]+1), answer='null'))
    if int(request.args['question']) == 19:
        return app.send_static_file(request.args['question']+'.html')
    if md5(request.args['answer']).hexdigest() == questionanswers[int(request.args['question'])-1]:
        db.editscore(session['username'])
        return redirect(
            url_for('quiz', question=str(int(request.args['question'])+1), answer='null'))
    if not db.getuser(session['username'])[7]:
        db.setstarttime(session['username'])
    return app.send_static_file(request.args['question']+'.html', )


@app.route('/userhome')
def userhome():
    return index()


@app.route('/confirm')
def confirm():
    try:
        db.confirm_createuser(
            json.loads(
                simplecrypt.decrypt(app.secret_key, request.args['token'])))
        return 'successfully confirmed'
    except:
        return 'not a valid token'


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        try:
            db.createuser(
                username=request.form['username'],
                passhash=md5(request.form['password']).hexdigest(),
                email=request.form['email'],
                college=request.form['college'],
                token=md5(request.form['username']+request.form['password']).hexdigest()
            )
            # return redirect(url_for('login'))
            return (
                'a conformation email is sent to your email\n'
                'please conform yourself through that link,\n'
                'don\'t forget check your spam\n'
            )
        except ValueError:
            return '''
        sorry! either the username is already registered or
        you have used non alphabetical characters.
        '''
    return app.send_static_file('register.html')
'''
<form action="" method="post">
    <p><input type=text name=username></p>
    <p><input type=text name=password></p>
    <p><input type=text name=email></p>
    <p><input type=text name=college></p>
    <p><input type=submit value=register></p>
</form>
'''


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        u = db.getuser(request.form['username'])
        print u
        if not u:
            return redirect(url_for('login'))
        if u[1] == md5(request.form['password']).hexdigest():
            session['username'] = request.form['username']
            return redirect(url_for('userhome'))
        else:
            return redirect(url_for('login'))

    return app.send_static_file('login.html')
'''
<form action="" method="post">
    <p><input type=text name=username></p>
    <p><input type=text name=password></p>
    <p><input type=submit value=Login></p>
</form>
'''


@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('userhome'))


# finally
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)
